package com.kgc.sbt.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.kgc.sbt.api.UserApi;
import com.kgc.sbt.config.annotation.RequestPermission;
import com.kgc.sbt.domain.User;
import com.kgc.sbt.domain.UserTT;
import com.kgc.sbt.util.RequestResult;
import com.kgc.sbt.util.ResultBuildUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

/**
 * @Auther: Administrator
 * @Date: 2021年 06月22日 09:15
 * @Description: 用户控制类
 */
@RestController
@Slf4j
@Api(tags = "用户注册登录操作接口，版本号：2.1.0")
public class UserController {

    @Reference
    private UserApi userApi;

    @Autowired
    private JdbcTemplate jdbcTemplate;


    // 静态注入redis
    private static Jedis jedis = new Jedis("127.0.0.1", 6379);

    static {
        // 用0库进行测试
        jedis.select(0);
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/22 0022 9:16
     * 功能描述: 模拟手机获取验证码
     */
    @GetMapping("/SendSMS")
    @ApiOperation(value = "用户手机号发送验证码", httpMethod = "GET", notes = "模拟手机获取验证码, 输入手机号（只进行了简单的11位数字校验，真实的场景下手机号一定是正确的）可以获取一个有效期为60秒的验证码，" +
            "该验证码不可复用，且此手机号在60秒内只能连续获取3次验证码")
    @ApiImplicitParam(name = "phoneNo", required = true, value = "用户手机号")
    @ApiResponses({
            @ApiResponse(code = 7466, message = "非法的手机号，请检查。"),
            @ApiResponse(code = 7312, message = "60秒内，获取验证码超出3次，禁止发送，请稍后重试。")
    })
    public RequestResult<String> getValidation(@RequestParam String phoneNo) {
        // 手机号简单非法判断
        if (phoneNo.length() != 11 || !isNumeric(phoneNo)) {
            return ResultBuildUtil.fail("7466", "非法的手机号，请检查。");
        }
        String countKey = phoneNo + ":count";
        String codeKey = phoneNo + ":code";
        // 获取指定的电话号码发送的验证码次数
        String cnt = jedis.get(countKey);
        // 对验证次数进行判断
        if (cnt == null || Integer.parseInt(cnt) < 3) {
            // 没有发送过验证码,生成一个60秒的次数key
            if (cnt == null) jedis.setex(countKey, 60, "0");
            // 模拟随机生成一个六位数验证码并模拟发送验证码
            StringBuffer code = new StringBuffer();
            for (int i = 0; i < 6; i++) {
                code.append(new Random().nextInt(10));
            }
            // 缓存中添加验证码，60秒失效
            jedis.setex(codeKey, 60, code.toString());
            // 递增手机发送数量
            jedis.incr(countKey);
            return ResultBuildUtil.success("你的验证码是：[" + code + "], 有效期为60秒。");
        } else {
            //返回超出3次，禁止发送
            return ResultBuildUtil.fail("7312", "60秒内，获取验证码超出3次，禁止发送，请稍后重试。");
        }
    }


    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/30 0030 9:35
     * 功能描述: 手机号数字校验
     */
    public static boolean isNumeric(String str) {
        for (int i = str.length(); --i >= 0; ) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/23 0023 14:53
     * 功能描述: 手机用户唯一校验
     */
    public boolean verifyUser(String phone) {
        return StringUtils.isEmpty(userApi.queryUser(phone));
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/22 0022 10:38
     * 功能描述: 模拟输入正确验证码后，手机注册成功
     */
    @PostMapping("/register")
    @ApiOperation(value = "用户发送验证码，注册", httpMethod = "POST", notes = "输入手机号和正确验证码后（验证码必须是与该手机号匹配的，并且还未失效的），手机注册成功（此手机号必须是还没有注册过的）。" +
            "用户注册成功后，验证码失效，用户初始密码为手机号后六位，并注册为普通会员。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", required = true, value = "验证码"),
            @ApiImplicitParam(name = "phoneNo", required = true, value = "用户手机号")
    })
    @ApiResponses({
            @ApiResponse(code = 7466, message = "非法的手机号，请检查。"),
            @ApiResponse(code = 6001, message = "手机号未填写正确或验证码已失效"),
            @ApiResponse(code = 6003, message = "验证码错误，请重试！"),
            @ApiResponse(code = 5010, message = "该手机已被注册！")
    })
    public RequestResult<String> register(@RequestParam String code, @RequestParam String phoneNo) {
        // 手机号非法判断
        if (phoneNo.length() != 11 || !isNumeric(phoneNo)) {
            return ResultBuildUtil.fail("7466", "非法的手机号，请检查。");
        }
        // 手机号唯一校验
        if (verifyUser(phoneNo)) {
            // 获取验证码
            String codeKey = phoneNo + ":code";
            String validation = jedis.get(codeKey);

            // 验证码非空判断
            if (validation == null) {
                return ResultBuildUtil.fail("6001", "手机号未填写正确或验证码已失效");
            }
            // 验证码有效性判断
            if (validation.equals(code)) {
                // 添加注册用户
                userApi.addUser(User.builder()
                        .phone(phoneNo)
                        .nickname("U" + phoneNo)
                        .password(phoneNo.substring(5))
                        .build());
                // 初始化会员信息表，为普通会员
                userApi.createCommonVip(phoneNo);
                // 注册成功，删除验证码
                jedis.del(codeKey);
                return ResultBuildUtil.success("注册成功！初始密码为手机号后六位！");
            } else {
                return ResultBuildUtil.fail("6003", "验证码错误，请重试！");
            }
        } else {
            return ResultBuildUtil.fail("5010", "该手机已被注册！");
        }
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/22 0022 13:42
     * 功能描述: 模拟输入正确验证码后，手机登录成功，生成token存入redis
     */
    @GetMapping("/loginBySms")
    @ApiOperation(value = "用户手机验证码，登录", httpMethod = "GET", notes = "输入手机号和正确验证码后（验证码必须是与该手机号匹配的，并且还未失效的），用户登录成功（此手机号必须是已经注册过的）。" +
            "用户登录成功后，验证码失效，并生成一个token。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", required = true, value = "验证码"),
            @ApiImplicitParam(name = "phoneNo", required = true, value = "用户手机号")
    })
    @ApiResponses({
            @ApiResponse(code = 7466, message = "非法的手机号，请检查。"),
            @ApiResponse(code = 6001, message = "手机号未填写正确或验证码已失效"),
            @ApiResponse(code = 5009, message = "该手机号未注册"),
            @ApiResponse(code = 6003, message = "验证码错误，请重试！")
    })
    public RequestResult<Map<String, Object>> loginBySms(@RequestParam String phoneNo, @RequestParam String code) {
        // 手机号非法判断
        if (phoneNo.length() != 11 || !isNumeric(phoneNo)) {
            return ResultBuildUtil.fail("7466", "非法的手机号，请检查。");
        }
        String codeKey = phoneNo + ":code";
        // 获取验证码
        String validation = jedis.get(codeKey);

        // 验证码非空判断
        if (validation == null) {
            return ResultBuildUtil.fail("6001", "手机号未填写正确或验证码已失效");
        }

        // 验证码有效性判断
        if (validation.equals(code)) {
            // 验证登录，用户是否存在
            if (verifyUser(phoneNo)) {
                return ResultBuildUtil.fail("5009", "该手机号未注册");
            } else {
                // 登录成功，删除验证码
                jedis.del(codeKey);

                // 获取用户Id
                String sql = "select id from user where phone = " + phoneNo;
                int userId = jdbcTemplate.queryForObject(sql, Integer.TYPE);
                // 生成用户token,存入redis (key-token, value-用户信息)
                String token = UUID.randomUUID().toString();
                // 生成token，存入redis （key-token, value-用户信息（转json字符串））
                jedis.set(token, JSON.toJSONString(userId));

                // 生成一个vipToken，用于检查VIP是否过期
                jedis.set("vipToken", phoneNo);

                Map<String, Object> map = new HashMap<>();
                map.put("msg", "登录成功");
                map.put("token", token);
                return ResultBuildUtil.success(map);
            }
        } else {
            return ResultBuildUtil.fail("6003", "验证码错误，请重试！");
        }
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/22 0022 14:21
     * 功能描述: 模拟输入正确的密码后，手机用户登录成功，生成token存入redis
     */
    @GetMapping("/loginByPwd")
    @ApiOperation(value = "用户密码，登录", httpMethod = "GET", notes = "输入手机号和正确密码后（初始为手机号后六位），用户登录成功（此手机号必须是已经注册过的）。" +
            "用户登录成功后，生成一个token。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phoneNo", required = true, value = "用户手机号"),
            @ApiImplicitParam(name = "password", required = true, value = "用户密码")
    })
    @ApiResponses({
            @ApiResponse(code = 7466, message = "非法的手机号，请检查。"),
            @ApiResponse(code = 5001, message = "用户名或密码错误"),
    })
    public RequestResult<Map<String, Object>> loginByPwd(@RequestParam String phoneNo, @RequestParam String password) {
        // 手机号非法判断
        if (phoneNo.length() != 11 || !isNumeric(phoneNo)) {
            return ResultBuildUtil.fail("7466", "非法的手机号，请检查。");
        }
        // 验证登录
        if (StringUtils.isEmpty(userApi.queryUserByPwd(phoneNo, password))) {
            return ResultBuildUtil.fail("5001", "用户名或密码错误");
        }

        String sql = "select id from user where phone = " + phoneNo;
        int userId = jdbcTemplate.queryForObject(sql, Integer.TYPE);
        // 生成用户token,存入redis (key-token, value-用户信息)
        String token = UUID.randomUUID().toString();

        // 生成token，存入redis （key-token, value-用户信息（转json字符串））
        jedis.set(token, JSON.toJSONString(userId));

        // 生成一个vipToken，用于检查VIP是否过期
        jedis.set("vipToken", phoneNo);

        Map<String, Object> map = new HashMap<>();
        map.put("msg", "登录成功");
        map.put("token", token);
        return ResultBuildUtil.success(map);
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/17 0017 15:08
     * 功能描述: 登出
     */
    @GetMapping("/logout")
    @ApiOperation(value = "退出登录", httpMethod = "GET", notes = "用户登录后，可以删除token来进行登出（需要该用户登录生成的token）")
    @RequestPermission
    @ApiResponses({
            @ApiResponse(code = 2001, message = "退出失败，token为空")
    })
    public RequestResult<String> doLogout(HttpServletRequest request) {
        // 从前端获取token, 从redis中删除token
        String token = request.getHeader("token");

        // token有效性判断
        if (StringUtils.isEmpty(token)) {
            return ResultBuildUtil.fail("2001", "退出失败，token为空");
        }

        // 从redis中删除token
        jedis.del(token);
        return ResultBuildUtil.success("用户退出成功!");
    }

    /**
     * @param:
     * @return:
     * @auther: hcc
     * @date: 2021/6/23 0023 11:55
     * 功能描述: 用户充值会员，可选10天、20天
     */
    @PostMapping("/chargeVipOfTenDays")
    @ApiOperation(value = "用户会员充值", httpMethod = "POST", notes = "用户登录后，可以选择充值的会员天数，type为1时是1分钱充值10天会员，type为2时是2分钱充值20天会员。" +
            "发起充值后，后台生成一个会员充值订单号，（测试接口时需要手动复制到微信支付接口）通过微信支付接口来进行生成订单支付二维码，扫码支付成功后，此接口会返回‘充值成功’,如果支付超时（5分钟）,则此接口返回‘充值失败’，并删除会员充值订单号。")
    @ApiImplicitParam(name = "type", required = true, value = "会员充值天数类型")
    @RequestPermission
    @ApiResponses({
            @ApiResponse(code = 5009, message = "该用户未注册"),
            @ApiResponse(code = 4556, message = "支付超时，未充值成功")
    })
    public RequestResult<String> chargeVipOfTenDays(HttpServletRequest request, @RequestParam Integer type) throws InterruptedException {

        String token = request.getHeader("token");
        String phoneNo = jdbcTemplate.queryForObject("select phone from user where id = " + jedis.get(token), String.class);

        // 验证该手机号是否存在,(此处冗余, 之后版本需要删除)
        if (verifyUser(phoneNo)) {
            return ResultBuildUtil.fail("5009", "该用户未注册");
        } else if (1 == type) {
            // 生成会员充值订单,并返回一个订单号
            String tradeNo = userApi.chargeVipOfTenDays(phoneNo);
            log.info("================== 充值订单号为:{} ========", tradeNo);

            // 存入redis锁，等待支付完成,锁定5分钟
            jedis.setex(tradeNo, 300, "等待支付中");
            // 判断微信支付是否成功
            while (true) {
                if ("success".equals(jedis.get(tradeNo))) {
                    break;
                } else if (jedis.get(tradeNo) == null) {
                    //  删除此条会员充值记录
                    userApi.delVipRecord(tradeNo);
                    return ResultBuildUtil.fail("4556", "支付超时，未充值成功");
                }
                log.info("+++++++++{}。。。。。。。。。。++++++++", jedis.get(tradeNo));
                Thread.sleep(3000);
            }

            // 判断该用户是否已经是会员
            if (0 == userApi.queryVip(phoneNo).getType()) {
                // 变更为会员，并增加十天
                userApi.updateVip(phoneNo);
            } else {
                // 加十天
                userApi.addVipTenDays(phoneNo);
            }
            return ResultBuildUtil.success("充值10天会员成功");
        } else {
            // 生成会员充值订单,并返回一个订单号
            String tradeNo = userApi.chargeVipOfTwentyDays(phoneNo);
            log.info("================== 充值订单号为:{} ========", tradeNo);

            // 存入redis锁，等待支付完成,锁定30秒（方便演示支付超时）
            jedis.setex(tradeNo, 30, "等待支付中");

            while (true) {
                if ("success".equals(jedis.get(tradeNo))) {
                    break;
                } else if (jedis.get(tradeNo) == null) {
                    //  删除此条会员充值记录
                    userApi.delVipRecord(tradeNo);
                    return ResultBuildUtil.fail("4556", "支付超时，未充值成功");
                }
                log.info("+++++++++{}。。。。。。。。。。++++++++", jedis.get(tradeNo));
                Thread.sleep(3002);
            }
            // 判断该用户是否已经是会员
            if (0 == userApi.queryVip(phoneNo).getType()) {
                // 变更为会员，并增加十天
                userApi.updateVip(phoneNo);
                // 再加十天
                userApi.addVipTenDays(phoneNo);
            } else {
                // 加二十天
                userApi.addVipTenDays(phoneNo);
                userApi.addVipTenDays(phoneNo);
            }
            return ResultBuildUtil.success("充值20天会员成功");

        }
    }

    /**
     * Created with IntelliJ IDEA.
     *
     * @Author: tianzw
     * @Description:修改手机号，密码，生日，性别，昵称等
     */
    @PostMapping("/updateInfo")
    @ApiOperation(value = "修改个人信息", notes = "修改个人所有信息")
    @RequestPermission
    public RequestResult<String> updateUser(UserTT user,HttpServletRequest request) {


        // 校验权限，是否已经登录
        String token = request.getHeader("token");
        Integer userId = (Integer) JSON.parse(jedis.get(token));
        user.setId(userId);
        log.info("------ userId:" + userId);

        return userApi.updateInfo(user) ? ResultBuildUtil.success("修改成功") : ResultBuildUtil.fail("9001", "修改失败，该号码已被别的用户绑定");
    }


    /**
     * Created with IntelliJ IDEA.
     *
     * @Author: tianzw
     * @Description:查询用户信息
     */
    @PostMapping("/selectInfo")
    @ApiOperation(value = "查询用户信息", notes = "通过传来的token查询用户信息")
    @RequestPermission
    public RequestResult<Map> selectInfo(HttpServletRequest request) {

        // 校验权限，是否已经登录
        String token = request.getHeader("token");

        // 校验token是否有效
        Integer userId = (Integer) JSON.parse(jedis.get(token));

        Map<String, Object> map = new HashMap<>();

        User user = userApi.selectInfoById(userId);

        map.put("用户编号", user.getId());
        map.put("手机号", user.getPhone());
        map.put("昵称", user.getNickname());
        map.put("生日",user.getBirthday());
        map.put("性别", user.getSex());
        map.put("邮箱", user.getEmail());
        map.put("头像", user.getHeadUrl());
        if (user == null) {
            return ResultBuildUtil.fail("8001", "没有该用户");
        }
        return ResultBuildUtil.success(map);
    }


}



